use std::sync::Arc;
use tonic::{Request, Response, Status};

use crate::grpc::reservation::reservation_service_server::ReservationService;
use crate::grpc::reservation::{
    CancelReservationRequest, CreateReservationRequest, GetReservationRequest,
    ListReservationsRequest, ListReservationsResponse, ReservationResponse,
    UpdateReservationStatusRequest,
};
use crate::service::ReservationService as ReservationServiceImpl;

pub struct GrpcReservationService {
    service: Arc<ReservationServiceImpl>,
}

impl GrpcReservationService {
    pub fn new(service: Arc<ReservationServiceImpl>) -> Self {
        Self { service }
    }
}

#[tonic::async_trait]
impl ReservationService for GrpcReservationService {
    async fn create_reservation(
        &self,
        request: Request<CreateReservationRequest>,
    ) -> Result<Response<ReservationResponse>, Status> {
        let req = request.into_inner();
        
        let check_in_date = chrono::NaiveDate::parse_from_str(&req.check_in_date, "%Y-%m-%d")
            .map_err(|_| Status::invalid_argument("Invalid check_in_date format, expected YYYY-MM-DD"))?;
        
        let check_out_date = chrono::NaiveDate::parse_from_str(&req.check_out_date, "%Y-%m-%d")
            .map_err(|_| Status::invalid_argument("Invalid check_out_date format, expected YYYY-MM-DD"))?;

        let reservation = self.service
            .create_reservation(
                req.guest_name,
                req.guest_email,
                req.guest_phone,
                req.room_number,
                check_in_date,
                check_out_date,
                req.total_amount,
            )
            .await
            .map_err(|e| Status::internal(format!("Failed to create reservation: {}", e)))?;

        let grpc_reservation = ReservationServiceImpl::model_to_grpc(reservation);
        Ok(Response::new(ReservationResponse {
            reservation: Some(grpc_reservation),
        }))
    }

    async fn get_reservation(
        &self,
        request: Request<GetReservationRequest>,
    ) -> Result<Response<ReservationResponse>, Status> {
        let req = request.into_inner();
        let id = req.id.parse::<i32>()
            .map_err(|_| Status::invalid_argument("Invalid reservation ID"))?;

        let reservation = self.service
            .get_reservation(id)
            .await
            .map_err(|e| Status::internal(format!("Failed to get reservation: {}", e)))?;

        match reservation {
            Some(reservation) => {
                let grpc_reservation = ReservationServiceImpl::model_to_grpc(reservation);
                Ok(Response::new(ReservationResponse {
                    reservation: Some(grpc_reservation),
                }))
            }
            None => Err(Status::not_found("Reservation not found")),
        }
    }

    async fn update_reservation_status(
        &self,
        request: Request<UpdateReservationStatusRequest>,
    ) -> Result<Response<ReservationResponse>, Status> {
        let req = request.into_inner();
        let id = req.id.parse::<i32>()
            .map_err(|_| Status::invalid_argument("Invalid reservation ID"))?;

        let status = req.status();
        let reservation = self.service
            .update_reservation_status(id, status)
            .await
            .map_err(|e| Status::internal(format!("Failed to update reservation status: {}", e)))?;

        match reservation {
            Some(reservation) => {
                let grpc_reservation = ReservationServiceImpl::model_to_grpc(reservation);
                Ok(Response::new(ReservationResponse {
                    reservation: Some(grpc_reservation),
                }))
            }
            None => Err(Status::not_found("Reservation not found")),
        }
    }

    async fn list_reservations(
        &self,
        request: Request<ListReservationsRequest>,
    ) -> Result<Response<ListReservationsResponse>, Status> {
        let req = request.into_inner();
        let page = if req.page > 0 { req.page as u64 } else { 1 };
        let per_page = if req.per_page > 0 { req.per_page as u64 } else { 10 };

        let (reservations, total) = self.service
            .list_reservations(
                page,
                per_page,
                req.guest_name,
                req.room_number,
                req.status.map(|s| {
                        match s {
                            0 => "PENDING",
                            1 => "PAID",
                            2 => "CHECKED_IN",
                            3 => "CHECKED_OUT",
                            4 => "CANCELLED",
                            _ => "PENDING",
                        }.to_string()
                    }),
            )
            .await
            .map_err(|e| Status::internal(format!("Failed to list reservations: {}", e)))?;

        let grpc_reservations: Vec<_> = reservations
            .into_iter()
            .map(ReservationServiceImpl::model_to_grpc)
            .collect();

        Ok(Response::new(ListReservationsResponse {
            reservations: grpc_reservations,
            total: total as i32,
            page: page as i32,
            per_page: per_page as i32,
        }))
    }

    async fn cancel_reservation(
        &self,
        request: Request<CancelReservationRequest>,
    ) -> Result<Response<ReservationResponse>, Status> {
        let req = request.into_inner();
        let id = req.id.parse::<i32>()
            .map_err(|_| Status::invalid_argument("Invalid reservation ID"))?;

        let reservation = self.service
            .cancel_reservation(id, req.reason)
            .await
            .map_err(|e| Status::internal(format!("Failed to cancel reservation: {}", e)))?;

        match reservation {
            Some(reservation) => {
                let grpc_reservation = ReservationServiceImpl::model_to_grpc(reservation);
                Ok(Response::new(ReservationResponse {
                    reservation: Some(grpc_reservation),
                }))
            }
            None => Err(Status::not_found("Reservation not found")),
        }
    }
}